#include "SysInfo.h"

void SysInfo::StartWork()
{
	if (mIsRunning)
		return;

	mIsRunning = true;	

	mThread = std::thread(&SysInfo::function_work,this);	
}

void SysInfo::StopWork()
{
	if (mIsRunning)
	{
		mIsRunning = false;
		mThread.join();
	}		
}

void SysInfo::function_work()
{
	while (mIsRunning)
	{
		if (mSysInfoCb)
		{
			//int pct_cpu = getUsage_CPU();
			int pct_cpu = getUsage_CPU_v2();
			int pct_memory = getUsage_Memory();
			int pct_gpu = getUsage_GPU();
			auto strNow = StringTool::GetStrTime_Now();

			ULARGE_INTEGER avail;
			ULARGE_INTEGER total;
			ULARGE_INTEGER free;

			avail.QuadPart = 0;
			total.QuadPart = 0;
			free.QuadPart = 0;

			auto bSuc = GetDiskFreeSpaceExW(NULL,&avail,&total,&free);

			mSysInfoCb(strNow, pct_cpu, pct_memory, pct_gpu, avail, total);
		}
		
		std::this_thread::sleep_for(std::chrono::milliseconds(1000));
	}
}

int SysInfo::getUsage_Memory()
{
	MEMORYSTATUSEX statusex;
	statusex.dwLength = sizeof(statusex);
	GlobalMemoryStatusEx(&statusex);

	return statusex.dwMemoryLoad;
}

void SysInfo::getDiskInfo(uint64_t& ulFree, uint64_t& ulTotal)
{
	ULARGE_INTEGER avail;
	ULARGE_INTEGER total;
	ULARGE_INTEGER free;

	avail.QuadPart = 0;
	total.QuadPart = 0;
	free.QuadPart = 0;

	auto bSuc = GetDiskFreeSpaceExW(NULL, &avail, &total, &free);

	ulFree = free.QuadPart;
	ulTotal = total.QuadPart;
}

//时间转换
int64_t Filetime2Int64(const FILETIME* ftime)
{
	LARGE_INTEGER li;
	li.LowPart = ftime->dwLowDateTime;
	li.HighPart = ftime->dwHighDateTime;
	return li.QuadPart;
}
//两个时间相减运算
int64_t Compare2FileTime(FILETIME preTime, FILETIME nowTime)
{
	return Filetime2Int64(&nowTime) - Filetime2Int64(&preTime);
}

int SysInfo::getUsage_CPU()
{	
	FILETIME idleTime;
	FILETIME kernelTime;
	FILETIME userTime;

	auto res = GetSystemTimes(&idleTime, &kernelTime, &userTime);

	int64_t idle = Compare2FileTime(mPreIdleTime, idleTime);
	int64_t kernel = Compare2FileTime(mPreKernelTime, kernelTime);
	int64_t user = Compare2FileTime(mPreUserTime, userTime);

	auto nCpuRate = 100 * (kernel + user - idle) / (kernel + user);

	mPreIdleTime = idleTime;
	mPreKernelTime = kernelTime;
	mPreUserTime = userTime;
	
	return nCpuRate;
}

int SysInfo::getUsage_GPU()
{
	if (mGpuDevCount < 1)
		return 0;

	nvmlReturn_t result;
	int retVal = 0;

	for (int i = 0; i < mGpuDevCount; ++i) {
		nvmlDevice_t device;
		result = nvmlDeviceGetHandleByIndex(0, &device);
		if (result != NVML_SUCCESS)
			return 3;

		char device_name[NVML_DEVICE_NAME_BUFFER_SIZE];
		result = nvmlDeviceGetName(device, device_name, NVML_DEVICE_NAME_BUFFER_SIZE);
		if (result != NVML_SUCCESS)
			return 4;

		std::printf("Device %d: %s\n", i, device_name);

		nvmlUtilization_st device_utilization;
		result = nvmlDeviceGetUtilizationRates(device, &device_utilization);

		if (result != NVML_SUCCESS)
			return 5;

		std::printf("GPU Util: %u, Mem Util: %u\n", device_utilization.gpu, device_utilization.memory);

		retVal += device_utilization.gpu;
	}

	retVal = retVal / mGpuDevCount;

	return retVal;
}

//cmd:
//typeperf "\Processor Information(_Total)\% Processor Utility"
void SysInfo::CPU2_Init() {
	PdhOpenQuery(NULL, NULL, &cpuQuery);
	//PdhAddCounter(cpuQuery, L"\\Processor(_Total)\\% Processor Time", NULL, &cpuTotal);
	PdhAddCounter(cpuQuery, L"\\Processor Information(_Total)\\% Processor Utility", NULL, &cpuTotal);
	PdhCollectQueryData(cpuQuery);
}

double SysInfo::getUsage_CPU_v2() {
	PDH_FMT_COUNTERVALUE counterVal;

	PdhCollectQueryData(cpuQuery);
	PdhGetFormattedCounterValue(cpuTotal, PDH_FMT_DOUBLE, NULL, &counterVal);

	if (counterVal.doubleValue >= 100.f)
		return 100;
	else	
		return counterVal.doubleValue;
}

int SysInfo::InitNvml()
{
	nvmlReturn_t result;	

	result = nvmlInit();
	if (result != NVML_SUCCESS)
		return -1;

	result = nvmlDeviceGetCount(&mGpuDevCount);
	if (result != NVML_SUCCESS)
		return -2;

	return 0;
}